package com.example.test.rke;

import lombok.experimental.UtilityClass;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.transport.socket.nio.NioDatagramConnector;
import java.net.InetSocketAddress;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 门禁短报文工具类
 **/
@UtilityClass
public class WgUDPUtil {

    public final int COMMAND_PACKET_LENGTH = 64; // 命令报文长度(byte)
    public final byte TYPE = 0x17; // 类型
    public final int DEFAULT_CONTROLLER_PORT = 60000; // 默认控制器端口
    public final byte OPEN_DOOR_FUNCTION_ID = 0x40;

    private final AtomicLong xid = new AtomicLong(0);
    private Queue<byte[]> queue;
    private IoConnector connector;
    private ConnectFuture connectFuture;


    /**
     * 连接控制器
     **/
    public void open(String ip) throws InterruptedException {
        open(ip, DEFAULT_CONTROLLER_PORT);
    }

    /**
     * 连接控制器
     **/
    public void open(String ip, int port) throws InterruptedException {
        queue = new ConcurrentLinkedQueue<>();
        connector = new NioDatagramConnector();
        connector.setHandler(new WgUdpCommShortHandler(queue));
        connectFuture = connector.connect(new InetSocketAddress(ip, port));
        // 让子弹飞一会儿
        Thread.sleep(100);
    }

    /**
     * functionID: 功能ID
     * iDevSn: 设备序列号
     * 生成64字节指令包
     **/
    public byte[] getCommand(byte functionID, long iDevSn, byte[] data) {
        byte[] command = new byte[COMMAND_PACKET_LENGTH];
        for (int i = 0; i < data.length; i++) {
            command[i] = 0;
        }
        command[0] = TYPE; // 指令类型
        command[1] = functionID; // 功能ID
        System.arraycopy(longToByte(iDevSn), 0, command, 4, 4); // 设备序列号 4字节 4~7
        System.arraycopy(data, 0, command, 8, data.length); // 数据 56字节 8~63
        System.arraycopy(longToByte(xid.incrementAndGet()), 0, command, 40, 4); // xid 4字节 40~43
        return command;
    }

    /**
     * long -> byte[8]
     **/
    public byte[] longToByte(long num) {
        byte[] bytes = new byte[8];
        for (int i = 0; i < 8; i++) {
            bytes[i] = (byte) (num % 256); // 最右端8位 -> 1byte
            num >>= 8; // num右移8位(即1byte)
        }
        return bytes;
    }

    /**
     * 关闭通信连接
     **/
    public void close() {
        IoSession session = connectFuture.getSession();
        if (session != null) {
            session.close(true);
        }
        connector.dispose();
    }

    /**
     * 向门禁控制器发送命令
     **/
    public byte[] sendCommand(byte[] command) throws InterruptedException {
        IoSession session = connectFuture.getSession();
        long xid = getXidFromCommand(command);

        if (session != null && session.isConnected()) {
            IoBuffer buffer = IoBuffer.allocate(command.length);
            buffer.put(command);
            buffer.flip();
            session.write(buffer);
        }

        for (int i = 0; i < 10; i++) {
            if (queue.isEmpty()) {
                Thread.sleep(30);
            } else {
                byte[] result = queue.poll();
                // 类型一致  功能号一致  xid一致
                if ((result[0] == command[0]) && (result[1] == command[1]) && (xid == getXidFromCommand(result))) {
                    return result;
                }
            }
        }

        return null;
    }

    /**
     * 获取指令中的xid
     **/
    private long getXidFromCommand(byte[] command) {
        long xid = -1;
        if (command.length >= COMMAND_PACKET_LENGTH) {
            xid = bytesToLong(command, 40, 4);
        }
        return xid;
    }

    /**
     * byte[] -> long
     **/
    public long bytesToLong(byte[] bytes, int startIndex, int length) {
        if (length > 8 || length < 1)
            return -1L;

        long ret = byteToInt(bytes[startIndex + length - 1]);
        for (int i = 1; i < length; i++) {
            ret <<= 8; // 左移8位(1byte)
            ret += byteToInt(bytes[startIndex + length - 1 - i]);
        }
        return ret;
    }

    /**
     * 将带符号的byte -> 不带符号的int
     **/
    public int byteToInt(byte bt) {
        if (bt < 0) {
            return (bt + 256); // bt转换为无符号的int
        } else {
            return bt;
        }
    }

    /**
     * 开门
     **/
    public static boolean openDoor(long iDevSn, int doorNo) throws InterruptedException {
        byte[] data = new byte[1];
        data[0] = (byte) (doorNo & 0xff);

        byte[] command = getCommand(OPEN_DOOR_FUNCTION_ID, iDevSn, data);
        byte[] result = sendCommand(command);

        return byteToInt(result[8]) == 1;
    }


}
